/*-----------------------------------------------File Info------------------------------------------------
** File Name:               main.c 
** Created date:            2021.11.12
** author:                  YLY
** Version:                 V0.1
** Descriptions:            C程序入口
**--------------------------------------------------------------------------------------------------------
*/

/**				
*									代码规范
* Tab Size == 4;	
*
* 数据类型:	数据类型使用<stdint.h>定义的别名,不建议使用C标准全名->,unsigned char-uint8_t;
*
* 变量命名总则:	基本数据类->	
*								全局变量->以g开头，例uint8_t gSize,
*								局部变量->无需前缀，按常规命名。		
*
*           	构造类->	
*						指针->	在基本数据类型命名规则前在加p. 例uint8_t *pPointer; 
*						结构->	结构体必需与typedef配合使用,不推荐单独使用 
*								结构名->	以小写str开头,名称等一个字母大写. 
*								别名->	以小写ts开头,名称等一个字母大写.
*								变量->	以小写t开头,结构名称首字母大写,也可以把机构体名称加入到变量第二个单词.  
*								
*								例struct strData tTempTime; struct osTask tTempTime; 

*						枚举名->    以小写em开头,结构名称首字母大写. 例enum DATE eTempTime; 
*						标识符->    标识符通常属于常量,全部字母大写,用_连接. 
*						枚举变量->  以小写e开头,结构名称首字母大写. 例enum DATE eTempTime;
*						
*						共同体-> 以小写n开头,结构名称首字母大写. 例union DATE nTempTime; 
*              
*				   		  	 
* 常量和宏定义:	全部字母大写,单词间用下横杠连接"_";	
*
* 全局变量:	在类型修饰后,变量名前,加小写g(global)以区分,不允许以单个字母命名的变量,如i,j,k等等.
*			例uint32_t *pgKeyNum;	uint16_t *pgKeyNum;	uint8_t gKeyNum;
*
* 局部变量:	需要保证与全局变量名称不同,只有用于确认循环次数的允许使用i,j,k....单个字的变量.
*			
* 函数命名: 第1个单词首字母小写,第2个单词开始首字母大写;返回值是指针的以小p开头,
*			如 void mcuInit(void){xx}; uint8_t mcuInit(void){xx};	uint16_t * pMcuInit(void){xxx};
*			以下函数"任务Ta/事件Ev/定时Ti",第一个单词与子函数相同,第2个单词为函数的类型的首两个字母.
*			如 uint32 *pEvKeyScan(void){xx};
*			当函数不允许外部文件访问时,需要在函数名称前加static. 
*
* 注释:	对代码进行解释除代码外的注释采用 "/""*"  "*""/" 的方式,临时屏蔽某行或者某段代码使用"//",
*
* 大括号:采用C标准,不建议使用C++或JAVA标准
*        如  	if	推荐		|	if{	禁止
*				{               |  		xxxx;
*					xxxx;		|    }
*				}				|
*            
**/

/* 
*	bootload逻辑如下：
*	MCU启动时，默认启动bootload
*	在bootload中开请看门够。
*	读用户数据FLASH，检查是否在升级状态：
*		如果正常AP工作模式，直接跳转，如果跳转不成本，停留在bootload,检查升级
*		如果是其它模式，初始化bootload所需外设，检查升级，直到升级成本，可以正常跳转。
*	在bootload中每5秒未应答，会超时检查升级状态：
*		如果是正常AP工作状态，跳转至AP，如果跳转失败，重新跳转回bootload
*		如果是其它状态，重新跳转回bootload或者重启MCU，一直检查更新，直到成功。
*	
*/
#include "main.h"

#include "mainTask.h"
#include "key.h"

tsCfgUart tgCfgUart03 = DEV_UART0_3;
tsCfgUart tgCfgUart10 = DEV_UART1_0;


#if COMPILE_MODE != DEFAULT_APP
	extern int __uboot_start_addr;	//必需使用此变量才能链接uboot
#endif



/*!
    \brief      GPIO INIT
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void gpioInit(void)
{
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_GPIOC);	
	rcu_periph_clock_enable(RCU_GPIOF);	
	
	//GPIO_BC(GPIOA) = GPIO_PIN_ALL;	//置L = SET;
	GPIO_BOP(GPIOA) = GPIO_PIN_ALL;		//GPIO默认置H = RESET
	GPIO_BOP(GPIOB) = GPIO_PIN_ALL;
	GPIO_BOP(GPIOC) = GPIO_PIN_ALL;
	GPIO_BOP(GPIOF) = GPIO_PIN_ALL;
	
	RELAYx_OPEN(RELAY1);
	RELAYx_OPEN(RELAY2);
	RELAYx_OPEN(RELAY3);
	RELAYx_OPEN(RELAY4);
	LED1_ON();
	LED2_ON();
	
	//ADC 输入三态  PA0
	gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0|GPIO_PIN_1);	

	//SPI_SCK 输出
	gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_5);
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5);	

	//SPI_MISO 输入
	gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_6);
	
	//SPI_MOSI 输出
	gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_7);
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
	
	//RST_OUT 输出推挽
	gpio_bit_write(SOC_RST, SOC_RST_STOP);
	gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0);
	gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);	

	//RST_EN
	gpio_bit_write(RST_EN,RST_EN_STOP);
	gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_1);
	gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);	
	
	//继电器1 输出推挽
	gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_2);
	gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
	
	//开关信号 输入，弱上拉 SW1~4
	gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15);
	
	//开关信号 输入，弱上拉 SW7	
	gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_8);	
	
	//ADC指示灯 输出推挽
	gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_9|GPIO_PIN_10);
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9|GPIO_PIN_10);
	
	//BUZZ 输出推挽
	gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_11);
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11);	
	
	//开关信号 输入，弱上拉 SW8	
	gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_12);	
	
	//INT_OUT 输出推挽
	gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_15);
	gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);		

	//继电器2 3 4 输出推挽
	gpio_mode_set(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5);
	gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5);
	
	//开关信号 输入，弱上拉 SW5~6
	gpio_mode_set(GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_12|GPIO_PIN_8|GPIO_PIN_9);
		
}

/**
*	function:	
*	作用:	 创建任务
*	return :	no
*	返回:	
**/
void taskCreateAssemble(void)
{
	uartInit(&tgCfgUart03, 115200u, NULL, 0, NULL);
	uartInit(&tgCfgUart10, 115200u, NULL, 0, NULL);

	/*
      \arg        RCU_FLAG_V12RST: V12 domain power reset flag
      \arg        RCU_FLAG_OBLRST: option byte loader reset flag
      \arg        RCU_FLAG_EPRST: external pin reset flag
      \arg        RCU_FLAG_PORRST: power reset flag
      \arg        RCU_FLAG_SWRST: software reset flag
      \arg        RCU_FLAG_FWDGTRST: free watchdog timer reset flag
      \arg        RCU_FLAG_WWDGTRST: window watchdog timer reset flag
      \arg        RCU_FLAG_LPRST: low-power reset flag
		*/	

	MAIN_DBG("\r\n************Application**************\r\n");
	MAIN_DBG("sysFreq=%d\r\n",rcu_clock_freq_get(CK_SYS));
	MAIN_DBG("AHBFreq=%d\r\n",rcu_clock_freq_get(CK_AHB));
	MAIN_DBG("SystemCoreClock=%d\r\n",SystemCoreClock);
		//1.2V异常复位
	MAIN_DBG("OBLRST=%d\r\n",rcu_flag_get(RCU_FLAG_OBLRST));	//可选字节装载器复位
	MAIN_DBG("EPRST=%d\r\n",rcu_flag_get(RCU_FLAG_EPRST));		//外部引脚复位
	MAIN_DBG("PORRST=%d\r\n",rcu_flag_get(RCU_FLAG_PORRST));	//电源复位
	MAIN_DBG("SWRST=%d\r\n",rcu_flag_get(RCU_FLAG_SWRST));		//软件复位
	MAIN_DBG("FWDGTRST=%d\r\n",rcu_flag_get(RCU_FLAG_FWDGTRST));	//主看狗
	MAIN_DBG("WWDGTRST=%d\r\n",rcu_flag_get(RCU_FLAG_WWDGTRST));	//窗口看门狗
	MAIN_DBG("LPRST=%d\r\n",rcu_flag_get(RCU_FLAG_LPRST));			//欠压
	
	rcu_all_reset_flag_clear();
	
	/* 需要创建的线程 */
	/* 1-MAIN 主线程-其它线程不能处理的,全部由主线程处理 */
	gMainTaskId = createTask(mainTask);
}

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/


/**
*	function:	program entry
*	作用:	程序入口 
*	return :	no
*	返回:	部分编译器要求有返回值,可以返回类型是int,但实际运行时不会返回 ,
**/
int main(void)
{
	/* 硬件初始化 */
	gpioInit();	

	#if COMPILE_MODE != DEFAULT_APP
		nvic_vector_table_set(NVIC_VECTTAB_FLASH, APP_VECT_TAB_OFFSET);
		__enable_irq();
	#endif 
	
	/* 任务创建 */
	taskCreateAssemble();

	/* 任务调度 */
	osScheduler();	
	
	/*  正常来说程序不会运行到此处 */
	GLOBAL_ERR("\r\n\r\nOS ERR!!\r\n\r\n");
	#if COMPILE_MODE == APP_AND_BOOT	//__uboot_start_addr必须调用一次才能把镜像编译进来
		printf("%x",__uboot_start_addr);
	#endif 		
}



/* 动态内存申请失败钩子函数 */
void vApplicationMallocFailedHook(void)
{
	GLOBAL_ERR("\r\n\r\nMALLIC FAIL,vApplicationMallocFailedHook\r\n\r\n");
}


/*!
    \brief      this function handles NMI exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void NMI_Handler(void)
{
	GLOBAL_ERR("\r\n\r\n NMI_Handler \r\n\r\n");
}

/*!
    \brief      this function handles HardFault exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void HardFault_Handler(void)
{
	
	GLOBAL_ERR("\r\n\r\n HardFault_Handler \r\n\r\n");
    /* if Hard Fault exception occurs, go to infinite loop */
    while(1){
		;
    }
}

/*!
    \brief      this function handles SVC exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SVC_Handler(void)
{
	GLOBAL_ERR("\r\n\r\n SVC_Handler \r\n\r\n");
}

/*!
    \brief      this function handles PendSV exception
    \param[in]  none
    \param[out] none
    \retval     none
*/
void PendSV_Handler(void)
{
	GLOBAL_ERR("\r\n\r\n PendSV_Handler \r\n\r\n");
}

